package com.tbit.uqbike.client.util;

import com.tbit.uqbike.client.constant.MapConstant;
import com.tbit.uqbike.client.pojo.Point;
import com.tbit.uqbike.client.pojo.vo.MyLatLng;
import com.tbit.uqbike.client.pojo.vo.PointVo;
import com.tbit.uqbike.client.util.calibrate.GpsReverseCorrect;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 电子围栏工具类
 *
 * @author Leon
 * 2017年11月30日 上午9:11:14
 */
public class GeoUtil {

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Point> getPoints(String pointString) {
        List<Point> points = new ArrayList<Point>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                points.add(new Point(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }

        return points;
    }

    /**
     * Points转字符串
     *
     * @param points
     * @return
     */
    public static String getPointToString(List<Point> points) {
        StringBuilder result = new StringBuilder();
        int size = points.size();
        for (int i = 0; i < size; i++) {
            Point point = points.get(i);
            result.append(String.valueOf(point.getX())).append(",").append(String.valueOf(point.getY()));

            if (i != (size - 1)) {
                result.append(";");
            }
        }

        return result.toString();
    }

    /**
     * 获取电子围栏的中心
     *
     * @param points
     * @return
     */
    public static Point GetCenterPoint(List<Point> points) {
        //以下为简化方法（400km以内）
        int total = points.size();
        double lat = 0, lon = 0;
        for (Point p : points) {
            lat += p.getX() * Math.PI / 180;
            lon += p.getY() * Math.PI / 180;
        }
        lat /= total;
        lon /= total;
        return new Point(lat * 180 / Math.PI, lon * 180 / Math.PI);
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point  检测点
     * @param points 多边形的顶点
     * @return 点在多边形内返回true, 否则返回false
     */
    public static boolean IsPtInPoly(Point point, List<Point> points) {
        int N = points.size();
        boolean boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        int intersectCount = 0;//cross points count of x
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point p1, p2;//neighbour bound vertices
        Point p = point; //当前点

        p1 = points.get(0);//left vertex
        for (int i = 1; i <= N; ++i) {//check all rays
            if (p.equals(p1)) {
                return boundOrVertex;//p is an vertex
            }

            p2 = points.get(i % N);//right vertex
            if (p.getX() < Math.min(p1.getX(), p2.getX()) || p.getX() > Math.max(p1.getX(), p2.getX())) {//ray is outside of our interests
                p1 = p2;
                continue;//next ray left point
            }

            if (p.getX() > Math.min(p1.getX(), p2.getX()) && p.getX() < Math.max(p1.getX(), p2.getX())) {//ray is crossing over by the algorithm (common part of)
                if (p.getY() <= Math.max(p1.getY(), p2.getY())) {//x is before of ray
                    if (p1.getX().equals(p2.getX()) && p.getY() >= Math.min(p1.getY(), p2.getY())) {//overlies on a horizontal ray
                        return boundOrVertex;
                    }

                    if (p1.getY().equals(p2.getY())) {//ray is vertical
                        if (p1.getY().equals(p.getY())) {//overlies on a vertical ray
                            return boundOrVertex;
                        } else {//before ray
                            ++intersectCount;
                        }
                    } else {//cross point on the left side
                        double xinters = (p.getX() - p1.getX()) * (p2.getY() - p1.getY()) / (p2.getX() - p1.getX()) + p1.getY();//cross point of y
                        if (Math.abs(p.getY() - xinters) < precision) {//overlies on a ray
                            return boundOrVertex;
                        }

                        if (p.getY() < xinters) {//before ray
                            ++intersectCount;
                        }
                    }
                }
            } else {//special case when ray is crossing through the vertex
                if (p.getX().equals(p2.getX()) && p.getY() <= p2.getY()) {//p crossing over p2
                    Point p3 = points.get((i + 1) % N); //next vertex
                    if (p.getX() >= Math.min(p1.getX(), p3.getX()) && p.getX() <= Math.max(p1.getX(), p3.getX())) {//p.getX() lies between p1.getX() & p3.getX()
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;//next ray left point
        }

        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /***
     * 获取点到多边形的距离
     * @param point
     * @param points
     * @return
     */
    public static Double getDistance1(Point point, List<Point> points) {
        /** * 最近的边 */
        Double distanceShort = Double.MAX_VALUE;
        Integer len = points.size();
        Integer maxIndex = len - 1;
        for (int i = 0; i < len; i++) {
            double temp;
            //多边形中当前点
            Point currentPoint = points.get(i);
            Point nearPoint = maxIndex == i ? points.get(0) : points.get(i + 1);
            double a, b, c;
            a = getPointDistance(point, currentPoint);//点与多边形一点的距离
            b = getPointDistance(point, nearPoint);//点与多边形一点的距离
            c = getPointDistance(currentPoint, nearPoint);//多边形邻边距离
            if (a * a >= b * b + c * c) {
                temp = b;
            } else if (b * b >= c * c + a * a) {
                temp = a;
            } else {
                double l = (a + b + c) / 2;     //周长的一半
                double s = Math.sqrt(l * (l - a) * (l - b) * (l - c));  //海伦公式求面积
                Double ss = 2 * s / c;
                /**  c为斜边，最近距离*/
                temp = ss;
            }
            /** b为斜边 */
            if (distanceShort > temp) {
                distanceShort = temp;
                continue;
            }
        }
        return distanceShort;
    }


    /*** 即将使用
     * 获取点到多边形的距离
     * @param c
     * @param points
     * @return
     */
    public static Double getDistance(Point c, List<Point> points) {
        /** 最近的边 */
        Double distanceShort = null;
        Integer len = points.size();
        for (int i = 0; i < len; i++) {
            // 多边形中当前点
            Point a = points.get(i % points.size());
            Point b = points.get((i + 1) % points.size());
            double l = pt2LineDist(c, a, b);
            if (distanceShort == null) {
                distanceShort = l;
            } else {
                if (l < distanceShort) {
                    distanceShort = l;
                }
            }
        }
        return distanceShort;
    }

    /**
     * 返回c点到线段ab的最短距离
     *
     * @param c
     * @param a
     * @param b
     * @return
     */
    public static double pt2LineDist(Point c, Point a, Point b) {
        Point ab = new Point(b.getX() - a.getX(), b.getY() - a.getY());
        Point ac = new Point(c.getX() - a.getX(), c.getY() - a.getY());

        double r = ab.getX() * ac.getX() + ab.getY() * ac.getY();
        r /= getDis(a, b) * getDis(a, b);
        if (Double.isNaN(r)) {
            return Double.MAX_VALUE;
        }
        // 如果投影点在A外，则距离是AC之间距离
        if (r < 0) {
            return getPointDistance(a, c);
        }

        // 如果投影点在B外，则距离是BC之间距离
        if (r > 1) {
            return getPointDistance(b, c);
        }

        // 如果投影在AB线段上，则距离是C到投影点之间距离
        Point d = new Point(ab.getX(), ab.getY());
        d.setX(d.getX() * r);
        d.setY(d.getY() * r);
        d.setX(d.getX() + a.getX());
        d.setY(d.getY() + a.getY());
        return getPointDistance(c, d);
    }

    /**
     * 计算数学意义上的距离
     *
     * @param a
     * @param b
     * @return
     */
    private static double getDis(Point a, Point b) {
        double x = a.getX() - b.getX();
        double y = a.getY() - b.getY();
        return Math.sqrt(x * x + y * y);
    }


    /**
     * 获取两点之间的距离
     */
    public static double getPointDistance(Point p1, Point p2) {

        double lon1 = (Math.PI / 180) * p1.getX();
        double lon2 = (Math.PI / 180) * p2.getX();
        double lat1 = (Math.PI / 180) * p1.getY();
        double lat2 = (Math.PI / 180) * p2.getY();

        // 地球半径
        double R = 6371;
        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1))
                * R;
        //转换成千米（根据自己需求）

        return d * 1000;
    }

    /**
     * 获取两点距离
     *
     * @param lon
     * @param lat
     * @param lon2
     * @param lat2
     * @return
     */
    public static double getDistanceBy(double lon, double lat, double lon2, double lat2) {
        Point p1 = new Point(lon, lat);
        Point p2 = new Point(lon2, lat2);
        return getPointDistance(p1, p2);
    }

    /**
     * 获取两条线段之间夹角
     *
     * @param o 交点
     * @param s 输入点
     * @param e 正北方向点
     * @return
     */
    public static double getAg(Point o, Point s, Point e) {
        double cosfi = 0, fi = 0, norm = 0;
        double dsx = s.getX() - o.getX();
        double dsy = s.getY() - o.getY();
        double dex = e.getX() - o.getX();
        double dey = e.getY() - o.getY();

        cosfi = dsx * dex + dsy * dey;
        norm = (dsx * dsx + dsy * dsy) * (dex * dex + dey * dey);
        cosfi /= Math.sqrt(norm);

        if (cosfi >= 1.0) {
            return 0;
        }
        if (cosfi <= -1.0) {
            return Math.PI;
        }
        fi = Math.acos(cosfi);

        if (180 * fi / Math.PI < 180) {
            return 180 * fi / Math.PI;
        } else {
            return 360 - 180 * fi / Math.PI;
        }

    }

    /**
     * AB连线与正北方向的角度（顺时针0~360）
     *
     * @param A
     * @param B
     * @return
     */
    public static double getAngle(MyLatLng A, MyLatLng B) {
        double dx = (B.m_RadLo - A.m_RadLo) * A.Ed;
        double dy = (B.m_RadLa - A.m_RadLa) * A.Ec;
        double angle = 0.0;
        angle = Math.atan(Math.abs(dx / dy)) * 180 / Math.PI;
        double dLo = B.m_Longitude - A.m_Longitude;
        double dLa = B.m_Latitude - A.m_Latitude;
        if (dLo > 0 && dLa <= 0) {
            angle = (90 - angle) + 90;
        } else if (dLo <= 0 && dLa < 0) {
            angle = angle + 180;
        } else if (dLo < 0 && dLa >= 0) {
            angle = (90 - angle) + 270;
        }
        return angle;
    }


    /**
     * 获取逆向校准点
     */
    public static Point calibrateRecerseOffSet(double lo, double la, int mapType) {
        Point point = new Point(lo, la);
        if (MapConstant.MAP_BAIDU == mapType) {
            double[] latlng = GpsReverseCorrect.baiduToOri(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        } else if (MapConstant.MAP_GOOGLE == mapType) {
            double[] latlng = GpsReverseCorrect.googleToOri(la, lo);
            point.setX(latlng[1]);
            point.setY(latlng[0]);
        }

        return point;
    }

    public static double getMin(Point point, String points) {
        Double min = null;
        List<String> list = Arrays.asList(points.split(";"));
        if (list.size() > 0) {
            for (int i = 0; i < list.size() - 1; i++) {
                String s = list.get(i);
                String[] strings = s.split(",");
                String s1 = list.get(i + 1);
                String[] strings1 = s1.split(",");
                Point b = new Point(Double.parseDouble(strings[0]), Double.parseDouble(strings[1]));
                Point c = new Point(Double.parseDouble(strings1[0]), Double.parseDouble(strings1[1]));
                double tmp = pointToLine(point.getX(), point.getY(), b.getX(), b.getY(), c.getX(), c.getY());
                if (min == null) {
                    min = tmp;
                } else if (tmp < min) {
                    min = tmp;
                }
            }
        }
        return min;
    }

    // 点到直线的最短距离的判断 点（x0,y0） 到由两点组成的线段（x1,y1） ,( x2,y2 )
    public static double pointToLine(double x1, double y1, double x2, double y2, double x0, double y0) {
        double space = 0;
        double a, b, c;
        a = lineSpace(x1, y1, x2, y2);// 线段的长度
        b = lineSpace(x1, y1, x0, y0);// (x1,y1)到点的距离
        c = lineSpace(x2, y2, x0, y0);// (x2,y2)到点的距离
        if (c + b == a) {//点在线段上
            space = 0;
            return space;
        }
        if (a <= 0.000001) {//不是线段，是一个点
            space = b;
            return space;
        }
        if (c * c >= a * a + b * b) { //组成直角三角形或钝角三角形，(x1,y1)为直角或钝角
            System.out.println("组成直角三角形或钝角三角形，(x1,y1)为直角或钝角  ");
            space = b;
            return space;
        }
        if (b * b >= a * a + c * c) {//组成直角三角形或钝角三角形，(x2,y2)为直角或钝角
            System.out.println("组成直角三角形或钝角三角形，(x2,y2)为直角或钝角 ");
            space = c;
            return space;
        }
        //组成锐角三角形，则求三角形的高
        System.out.println("组成锐角三角形，则求三角形的高  ");
        double p = (a + b + c) / 2;// 半周长
        double s = Math.sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积
        space = 2 * s / a;// 返回点到线的距离（利用三角形面积公式求高）
        return space;
    }

    //计算两点之间的距离
    public static double lineSpace(double x1, double y1, double x2, double y2) {
        double lineLength = 0;
        lineLength = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        return lineLength;
    }


    /*****************************************************************************************************************/
    public static List<PointVo> getVo(String pointString) {
        List<PointVo> points = new ArrayList<PointVo>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                points.add(new PointVo(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }

        return points;
    }

    // 地球半径（单位：米）
    private static final double EARTH_RADIUS = 6371000;

    /**
     * 计算正方形的左下角和右上角的经纬度
     *
     * @param lat 中心点纬度（单位：度）
     * @param lng 中心点经度（单位：度）
     * @param radius 半径（单位：米）
     * @return 左下角和右上角的经纬度坐标
     */
    public static Point[] getBoundingBox(double lng, double lat, double radius) {
        // 将纬度和经度从度数转换为弧度
        double latRad = Math.toRadians(lat);

        // 纬度变化量（单位：度）
        double deltaLat = (radius / EARTH_RADIUS) * (180 / Math.PI);

        // 经度变化量（单位：度）
        double deltaLng = (radius / (EARTH_RADIUS * Math.cos(latRad))) * (180 / Math.PI);

        // 左下角点（纬度最小，经度最小）
        double minLat = lat - deltaLat;
        double minLng = lng - deltaLng;

        // 右上角点（纬度最大，经度最大）
        double maxLat = lat + deltaLat;
        double maxLng = lng + deltaLng;

        return new Point[] {
                new Point(minLng, minLat), // 左下角
                new Point(maxLng, maxLat)  // 右上角
        };
    }

    public static void main(String[] args) {
        List<Point> points = new ArrayList<Point>();
        String point = "113.300836,22.660083;113.692178,22.605937;113.861707,22.692896;114.990793,22.647686;114.70084,23.358358;114.010842,22.98038;113.764034,23.016098;113.279585,23.003495";
        for (String s : point.split(";")) {
            String[] split = s.split(",");
            points.add(new Point(Double.parseDouble(split[0]), Double.parseDouble(split[1])));
        }


        Double distance = getDistance(new Point(113.928254, 22.745006), points);
        System.out.println(distance);
    }
}
